package in;

import general.Killable;
import general.Logger;
import general.Main;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Map;

/**
 * Поток-сервер, слушающий порт. Создает потоки Worker для
 * обработки запросов.
 * 
 * @author evilenka@yandex.ru
 * 
 */

public class Daemon extends Thread implements Killable
{

	private Map<String, Map<String, String>> configs;
	private int port;
	private ServerSocket server;
	private ArrayList<Worker> workers;
	
	private volatile boolean  killed=false;
	private long ttl;

	/**
	 * Конструктор.
	 * 
	 * @param configs
	 *            - конфиги, посланные из Веба
	 * @throws DaemonException
	 * 				если нет соответствующих конфигов или нельзя забиндиться
	 */

	public Daemon(Map<String, Map<String, String>> configs) throws DaemonException
	{
		this.configs = configs;
		if(configs.containsKey("IN")&&configs.get("IN").containsKey("PORT")){
			this.port=Integer.parseInt(configs.get("IN").get("PORT"));
		}
		else{
			throw new DaemonException("Error in configs! Field PORT in section IN required!");
		}
		try {
			server=new ServerSocket(port);
		} catch (IOException e) {
			throw new DaemonException("Error! Could not create ServerSocket on port "+port);
		}
		this.workers=new ArrayList<Worker>();
	}

	/**
	 * Старт потока, выполняется Вебом.
	 * 
	 * 
	 */

	@Override
	public void run()
	{
		Logger logger=Main.getLogger();
		while (true)
		{
			if(killed){
				for(Worker w : workers){
					w.kill(ttl/2);
				}
				try {
					sleep(ttl/2);
				} catch (InterruptedException e1) {
					
				}
				for(Worker w : workers){
					if(w.isAlive()){
						w.stop();
					}
				}	
				try {
					server.close();
				} catch (IOException e) {
					logger.warn("Daemon could not close socket. "+e.getLocalizedMessage());
				}
				return;
			}
			try
			{
				Socket client = server.accept();
				InetAddress ip = client.getInetAddress();
				Worker worker = new Worker(client, configs);
				workers.add(worker);
				logger.info("New worker for client "
						+ ip.getHostAddress() + " started");
			} catch (IOException e)
			{
				logger.warn("Daemon server could not accept client. "+e.getLocalizedMessage());
			} catch (DaemonException e) {
				logger.warn("Daemon server could not create Worker. "+e.getLocalizedMessage());
			}

		}

	}
	
	/**
	 * Убийство.
	 * 
	 * @param milliseconds
	 * 				время до принудительной остановки
	 */

	public void kill(long milliseconds) {
		killed=true;
		ttl=milliseconds;
	}

}
